package com.clhbolg.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.easyes.core.conditions.LambdaEsQueryWrapper;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.clhbolg.constant.RedisConstant;
import com.clhbolg.domain.Article;
import com.clhbolg.domain.ArticleTag;
import com.clhbolg.domain.Category;
import com.clhbolg.domain.Tag;
import com.clhbolg.domain.bo.ArticleBo;
import com.clhbolg.domain.vo.ArticleVo;
import com.clhbolg.enums.ArticleStatusEnum;
import com.clhbolg.enums.FileExtEnum;
import com.clhbolg.enums.FilePathEnum;
import com.clhbolg.enums.OperationEnum;
import com.clhbolg.event.ArticleEventPush;
import com.clhbolg.mapper.ArticleMapper;
import com.clhbolg.mapper.ArticleTagMapper;
import com.clhbolg.mapper.CategoryMapper;
import com.clhbolg.mapper.TagMapper;
import com.clhbolg.mapper.es.ArticlesSearchMapper;
import com.clhbolg.model.dto.*;
import com.clhbolg.model.es.ArticleSearchDTO;
import com.clhbolg.model.vo.request.ArticlePasswordVO;
import com.clhbolg.model.vo.request.ArticleTopFeaturedVO;
import com.clhbolg.model.vo.request.ConditionVO;
import com.clhbolg.model.vo.request.LogicalDelVO;
import com.clhbolg.service.IArticleService;
import com.clhbolg.strategy.context.UploadStrategyContext;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.GlobalException;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.StringUtils;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 文章Service业务层处理
 *
 * @author cailinhu
 * @date 2023-02-07
 */
@RequiredArgsConstructor
@Service
@DS("slave")
@Slf4j
public class ArticleServiceImpl implements IArticleService {

    private final ArticleMapper baseMapper;

    @Resource
    private CategoryMapper categoryMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Resource
    private TagMapper tagMapper;

    @Resource
    private ArticleTagMapper articleTagMapper;

    @Autowired
    private UploadStrategyContext uploadStrategyContext;
    //
    @Autowired
    private ArticlesSearchMapper articlesSearchMapper;

    @Autowired
    private ArticleEventPush articleEventPush;

    /**
     * 查询文章
     */
    @Override
    public ArticleVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }

    /**
     * 查询文章列表
     */
    @Override
    public TableDataInfo<ArticleVo> queryPageList(ArticleBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<Article> lqw = buildQueryWrapper(bo);
        Page<ArticleVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询文章列表
     */
    @Override
    public List<ArticleVo> queryList(ArticleBo bo) {
        LambdaQueryWrapper<Article> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<Article> buildQueryWrapper(ArticleBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<Article> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getUserId() != null, Article::getUserId, bo.getUserId());
        lqw.eq(bo.getCategoryId() != null, Article::getCategoryId, bo.getCategoryId());
        lqw.eq(StringUtils.isNotBlank(bo.getArticleCover()), Article::getArticleCover, bo.getArticleCover());
        lqw.eq(StringUtils.isNotBlank(bo.getArticleTitle()), Article::getArticleTitle, bo.getArticleTitle());
        lqw.eq(StringUtils.isNotBlank(bo.getArticleContent()), Article::getArticleContent, bo.getArticleContent());
        lqw.eq(bo.getIsTop() != null, Article::getIsTop, bo.getIsTop());
        lqw.eq(bo.getIsFeatured() != null, Article::getIsFeatured, bo.getIsFeatured());
        lqw.eq(bo.getIsDelete() != null, Article::getIsDelete, bo.getIsDelete());
        lqw.eq(bo.getStatus() != null, Article::getStatus, bo.getStatus());
        lqw.eq(bo.getType() != null, Article::getType, bo.getType());
        lqw.eq(StringUtils.isNotBlank(bo.getPassword()), Article::getPassword, bo.getPassword());
        lqw.eq(StringUtils.isNotBlank(bo.getOriginalUrl()), Article::getOriginalUrl, bo.getOriginalUrl());
        return lqw;
    }

    /**
     * 新增文章
     */
    @Override
    public Boolean insertByBo(ArticleBo bo) {
        Article add = BeanUtil.toBean(bo, Article.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改文章
     */
    @Override
    public Boolean updateByBo(ArticleBo bo) {
        Article update = BeanUtil.toBean(bo, Article.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(Article entity) {
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 批量删除文章
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    /**
     * 保存的时候 ，顺带保存标签和分类
     *
     * @param vo
     */
    @Override
    @Transactional
    public void saveOrUpdateArticle(ArticleVo vo) {
        //新建或保存分类
        Category category = saveArticleCategory(vo);
        //保存文章信息
        Article article = Convert.convert(Article.class, vo);
        if (Objects.nonNull(category)) {
            article.setCategoryId(category.getId());
        }
        //获得单当前用户
        Long userId = LoginHelper.getUserId();
        article.setUserId(userId);
        baseMapper.insertOrUpdate(article);
        //保存标签等信息
        saveArticleTag(vo, article.getId());
        //TODO 一套结束完成。可以发送mq消息到订阅者
//        baseMapper.insertOrUpdate();
        //发布监听
        if (vo.getId() != null) {
            articleEventPush.articlePush(article, OperationEnum.UPDATE);
        } else {
            articleEventPush.articlePush(article, OperationEnum.INSERT);
        }
    }


    @Override
    public void updateArticleTopAndFeatured(ArticleTopFeaturedVO articleTopFeaturedVO) {
        Article article = Article.builder()
            .id(Convert.toLong(articleTopFeaturedVO.getId()))
            .isTop(articleTopFeaturedVO.getIsTop())
            .isFeatured(articleTopFeaturedVO.getIsFeatured())
            .build();
        baseMapper.updateById(article);
    }

    @Override
    public ArticleVo editArticle(Long articleId) {
        ArticleVo articleVo = baseMapper.selectVoById(articleId);
        //查询出对应的分类名称和标签名称
        if (Objects.nonNull(articleVo)) {
            Long categoryId = articleVo.getCategoryId();
            Category category = categoryMapper.selectById(categoryId);
            if (Objects.nonNull(category))
                articleVo.setCategoryName(category.getCategoryName());
            List<ArticleTag> articleTags = articleTagMapper.selectList(Wrappers.lambdaQuery(ArticleTag.class).eq(ArticleTag::getArticleId, articleId));
            if (!articleTags.isEmpty()) {
                List<Long> tagId = articleTags.stream().map(ArticleTag::getTagId).collect(Collectors.toList());
                List<String> collect = tagMapper.selectList(Wrappers.lambdaQuery(Tag.class).in(Tag::getId, tagId)).stream().map(Tag::getTagName).collect(Collectors.toList());
                articleVo.setTagNames(collect);
            }
        }
        return articleVo;
    }

    @Override
    public void delLogicalArticle(LogicalDelVO vo) {
        //逻辑删除或者恢复
        List<Article> collect = vo.getIds().stream().map(i -> {
            Article build = Article.builder().id(i).isDelete(vo.getIsDelete()).build();
            articleEventPush.articlePush(build, OperationEnum.UPDATE);
            return build;
        }).collect(Collectors.toList());
        baseMapper.updateBatchById(collect);
    }

    @Override
    @Transactional
    public void delArticles(List<Long> ids) {
        //1.删除文章和标签的关联关系
        articleTagMapper.delete(Wrappers.lambdaQuery(ArticleTag.class).in(ArticleTag::getArticleId, ids));
        //2、删除redis中的阅读量
        ids.forEach(id -> {
            redisTemplate.opsForZSet().remove(RedisConstant.ARTICLE_VIEWS_COUNT, id);
            articleEventPush.articlePush(Article.builder().id(id).build(), OperationEnum.DEL);
        });
        //3.删除数据表数据
        baseMapper.deleteBatchIds(ids);

    }

    @Override
    public List<String> articleExport(List<Long> ids) {
        //只要标题内容
        List<Article> articles = baseMapper.selectList(new LambdaQueryWrapper<Article>()
            .select(Article::getArticleTitle, Article::getArticleContent)
            .in(Article::getId, ids));
        List<String> urls = new ArrayList<>();
        for (Article article : articles) {
            try (ByteArrayInputStream inputStream = new ByteArrayInputStream(article.getArticleContent().getBytes())) {
                String url = uploadStrategyContext.executeUploadStrategy(article.getArticleTitle() + FileExtEnum.MD.getExtName(), inputStream, FilePathEnum.MD.getPath());
                urls.add(url);
            } catch (Exception e) {
                log.error("", e);
                throw new GlobalException("导出文章失败");
            }
        }
        return urls;
    }

    @Override
    public byte[] localArticlesExport(List<Long> ids, HttpServletResponse response) throws IOException {
        List<Article> articles = baseMapper.selectList(new LambdaQueryWrapper<Article>()
            .select(Article::getArticleTitle, Article::getArticleContent)
            .in(Article::getId, ids));
        ZipOutputStream zipOut = null;
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
            zipOut = new ZipOutputStream(outputStream);
            for (Article article : articles) {
                ByteArrayInputStream inputStream = new ByteArrayInputStream(article.getArticleContent().getBytes());
                byte[] bytes = IoUtil.readBytes(inputStream);
                ZipEntry zipEntry = new ZipEntry(article.getArticleTitle() + FileExtEnum.MD.getExtName());
                zipOut.putNextEntry(zipEntry);
                zipOut.write(bytes, 0, bytes.length);
                IoUtil.close(inputStream);
            }
            zipOut.finish();
        } catch (Exception e) {
            log.error("", e);
        } finally {
            if (zipOut != null) {
                zipOut.close();
            }
        }
        return outputStream.toByteArray();
    }

    @Override
    public void articlesImport(MultipartFile file, String type) {
        //文件名
        String articleTitle = Objects.requireNonNull(file.getOriginalFilename()).split("\\.")[0];
        String read = "";
        try (InputStream inputStream = file.getInputStream()) {
            read = IoUtil.read(inputStream, Charset.forName("utf-8"));
//            List<String> list = IoUtil.readLines(inputStream, Charset.forName("utf-8"), content);
        } catch (IOException e) {
            log.error(StrUtil.format("导入文章失败, 堆栈:{}", ExceptionUtil.stacktraceToString(e)));
            throw new GlobalException("导入文章失败");
        }
        //获取图片
//        InetAddress.getLocalHost()
//        String clientIP = ServletUtils.getClientIP();
//
//        String localMacAddress2 = NetUtil.getMacAddress(clientIP);


        ArticleVo articleVO = ArticleVo.builder()
            .articleTitle(articleTitle)
            .articleContent(read)
            .status(ArticleStatusEnum.DRAFT.getStatus())
            .build();
        saveOrUpdateArticle(articleVO);
    }

    /**
     * 获取置顶和推荐文章
     *
     * @return
     */
    @Override
    public TopAndFeaturedArticlesDTO listTopAndFeaturedArticles() {
        List<ArticleCardDTO> articleCardDTOs = baseMapper.listTopAndFeaturedArticles();
        if (articleCardDTOs.size() == 0) {
            return new TopAndFeaturedArticlesDTO();
        } else if (articleCardDTOs.size() > 3) {
            //推荐如果大于3就去前三个
            articleCardDTOs = articleCardDTOs.subList(0, 3);
        }
        //首页的最显目的位置展示
        TopAndFeaturedArticlesDTO topAndFeaturedArticlesDTO = new TopAndFeaturedArticlesDTO();
        topAndFeaturedArticlesDTO.setTopArticle(articleCardDTOs.get(0));
        articleCardDTOs.remove(0);
        //其余的普通列表
        topAndFeaturedArticlesDTO.setFeaturedArticles(articleCardDTOs);
        return topAndFeaturedArticlesDTO;
    }


    @Override
    public TableDataInfo<ArticleAdminDTO> articleList(ConditionVO vo, PageQuery pageQuery) {

        Page<ArticleAdminDTO> dtos = baseMapper.selectArticleList(vo, pageQuery.build());
        //需要在redis 中查询出浏览量
        Set<ZSetOperations.TypedTuple<Object>> set = redisTemplate.opsForZSet().rangeWithScores(RedisConstant.ARTICLE_VIEWS_COUNT, 0L, -1L);
        Map<Object, Double> collect = set.stream()
            .collect(Collectors.toMap(ZSetOperations.TypedTuple::getValue, ZSetOperations.TypedTuple::getScore));
        dtos.getRecords().forEach(a -> {
            Double aDouble = collect.get(a.getId());
            if (Objects.nonNull(aDouble))
                a.setViewsCount(aDouble.intValue());
        });
        return TableDataInfo.build(dtos);
    }


    @Override
    public TableDataInfo<ArticleCardDTO> userArticleList(ConditionVO vo, PageQuery pageQuery) {
        Page<ArticleCardDTO> dtos = baseMapper.userArticleList(vo, pageQuery.build());
        return TableDataInfo.build(dtos);
    }

    @Override
    public TableDataInfo<ArticleCardDTO> listArticlesByCategoryId(Integer categoryId, PageQuery pageQuery) {
        ConditionVO conditionVO = new ConditionVO();
        conditionVO.setCategoryId(categoryId);
        return userArticleList(conditionVO, pageQuery);
    }

    @Override
    @SneakyThrows
    public ArticleDTO getArticleById(Long id) {
        Article article = baseMapper.selectById(id);
        if (Objects.isNull(article)) {
            return null;
        }
        //TODO 判断私密  如果是私密的就判断是否已经访问过，如果没有访问过就要输入密码
        if (article.getStatus().equals(2)) {
            Boolean isAccess = redisTemplate.opsForSet().isMember(RedisConstant.ARTICLE_ACCESS + StpUtil.getLoginId(), id);
            if (isAccess.equals(false)) {
                throw new GlobalException("文章密码认证未通过");
            }
        }
        //再增加一次文章的访问量
        redisTemplate.opsForZSet().incrementScore(RedisConstant.ARTICLE_VIEWS_COUNT, id, 1D);
        ArticleDTO articleById = baseMapper.getArticleById(id);
        CompletableFuture<ArticleCardDTO> asyncPreArticle = CompletableFuture.supplyAsync(() -> {
            //查询上一篇文章
            ArticleCardDTO preArticle = baseMapper.getPreArticleById(id);
            if (Objects.isNull(preArticle)) {
                preArticle = baseMapper.getLastArticle();
            }
            return preArticle;
        });
        //查询下一篇文章
        CompletableFuture<ArticleCardDTO> asyncNextArticle = CompletableFuture.supplyAsync(() -> {
            ArticleCardDTO nextArticle = baseMapper.getNextArticleById(id);
            if (Objects.isNull(nextArticle)) {
                nextArticle = baseMapper.getFirstArticle();
            }
            return nextArticle;
        });
        Double score = redisTemplate.opsForZSet().score(RedisConstant.ARTICLE_VIEWS_COUNT, id);
        if (Objects.nonNull(score)) {
            articleById.setViewCount(score.intValue());
        }
        articleById.setPreArticleCard(asyncPreArticle.get());
        articleById.setNextArticleCard(asyncNextArticle.get());
        return articleById;
    }

    @Override
    public void accessArticle(ArticlePasswordVO articlePasswordVO) {
        Article article = baseMapper.selectById(articlePasswordVO.getArticleId());
        if (Objects.isNull(article)) {
            throw new GlobalException("文章不存在");
        }
        if (article.getPassword().equals(articlePasswordVO.getArticlePassword())) {
            redisTemplate.opsForSet().add(RedisConstant.ARTICLE_ACCESS + StpUtil.getLoginId(), articlePasswordVO.getArticleId());
        } else {
            throw new GlobalException("密码错误");
        }
    }

    @Override
    public void saveArticleToEs() {
        //查询出所有的公开未删除文章信息
        List<Article> articles = baseMapper.selectList(Wrappers.lambdaQuery(Article.class)
            .eq(Article::getStatus, 1).eq(Article::getIsDelete, 0));
        articlesSearchMapper.deleteBatchIds(articles);
        List<ArticleSearchDTO> list = Convert.toList(ArticleSearchDTO.class, articles);
        articlesSearchMapper.insertBatch(list);
    }

    @Override
    public void saveArticleToEs(Article article) {
        //直接保存
        ArticleSearchDTO convert = Convert.convert(ArticleSearchDTO.class, article);
        articlesSearchMapper.insert(convert);
    }

    @Override
    public void updateArticleToEs(Article article) {
        ArticleSearchDTO convert = Convert.convert(ArticleSearchDTO.class, article);
        articlesSearchMapper.updateById(convert);
    }

    @Override
    public List<ArticleSearchDTO> listArticleBySearch(ConditionVO condition) {
        LambdaEsQueryWrapper<ArticleSearchDTO> wrapper = new LambdaEsQueryWrapper<>();
//        wrapper.eq(ArticleSearchDTO::getIsDelete,0).eq(ArticleSearchDTO::getStatus,ArticleStatusEnum.PUBLIC.getStatus());
//        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
//        boolQuery.filter(QueryBuilders.termQuery("isDelete",0));
//        boolQuery.filter(QueryBuilders.termQuery("status",ArticleStatusEnum.PUBLIC.getStatus()));
//        boolQuery.must(QueryBuilders.matchQuery("articleTitle",condition.getKeywords()));
//        boolQuery.must(QueryBuilders.matchQuery("articleContent",condition.getKeywords()));
//        searchSourceBuilder.query(boolQuery);
        wrapper.eq(ArticleSearchDTO::getIsDelete, 0).eq(ArticleSearchDTO::getStatus, ArticleStatusEnum.PUBLIC.getStatus())
            .and(a->a.match(ArticleSearchDTO::getArticleTitle, condition.getKeywords())
                .or().match(ArticleSearchDTO::getArticleContent, condition.getKeywords()));
//        wrapper.setSearchSourceBuilder(searchSourceBuilder);
        return articlesSearchMapper.selectList(wrapper);
    }

    /**
     * 文章归档
     *
     * @return
     */
    @Override
    public TableDataInfo<ArchiveDTO> listArchives(PageQuery pageQuery) {
        Page<ArticleCardDTO> articlePage = baseMapper.listArchives(pageQuery.build());
        //按时间分组
        Map<String, List<ArticleCardDTO>> map = new HashMap<>();
        for (ArticleCardDTO article : articlePage.getRecords()) {
            LocalDateTime createTime = article.getCreateTime();
            int year = createTime.getYear();
            int month = createTime.getMonth().getValue();
            String key =year+"-"+month;
            if (map.containsKey(key)){
                List<ArticleCardDTO> articles = map.get(key);
                articles.add(article);
            }else {
                List<ArticleCardDTO> objects = new ArrayList<>();
                objects.add(article);
                map.put(key,objects);
            }
        }
        //还差个排序
        List<ArchiveDTO> archiveDTOs = map.keySet().stream().map(k -> ArchiveDTO.builder().Time(k).articles(map.get(k)).build())
            .sorted((v1,v2)->{
                //倒叙排列法
                DateTime date = DateUtil.parse(v1.getTime(), DatePattern.NORM_MONTH_PATTERN);
                DateTime date1 = DateUtil.parse(v2.getTime(),DatePattern.NORM_MONTH_PATTERN);
                return date1.compareTo(date);
            })
            .collect(Collectors.toList());
        TableDataInfo<ArchiveDTO> build = TableDataInfo.build(archiveDTOs);
        return build;
    }

    @Override
    public TableDataInfo<ArticleCardDTO> getArticleByTag(PageQuery pageQuery, Integer tagId) {
        TableDataInfo<ArticleCardDTO> articleCardDTOTableDataInfo = this.userArticleList(ConditionVO.builder().tagId(tagId).build(), pageQuery);
        return articleCardDTOTableDataInfo;
    }

    @Transactional
    public void saveArticleTag(ArticleVo vo, Long id) {
        //1. 删除原有的关联关系
        articleTagMapper.delete(Wrappers.lambdaQuery(ArticleTag.class).eq(ArticleTag::getArticleId, id));
        //2. 去重将要保存的标签
        List<String> tagNames = vo.getTagNames();
        if (CollectionUtil.isNotEmpty(tagNames)) {
            //已经存在标签
            List<Tag> tags = tagMapper.selectList(Wrappers.lambdaQuery(Tag.class).in(Tag::getTagName, tagNames));
            List<Long> tagIds = tags.stream().map(Tag::getId).collect(Collectors.toList());
            List<String> oldTagName = tags.stream().map(Tag::getTagName).collect(Collectors.toList());
            //删掉已经存在的标签名称 -->此时剩下的都是需要新增的标签
            tagNames.removeAll(oldTagName);
            //如果剩下的还有，就+一起保存关联关系
            if (!tagNames.isEmpty()) {
                //保存新标签
                List<Tag> newTags = tagNames.stream().map(t -> {
                    Tag tag = new Tag();
                    tag.setTagName(t);
                    return tag;
                }).collect(Collectors.toList());
                tagMapper.insertBatch(newTags);
                //收集关联关系
                List<Long> newTagIds = newTags.stream().map(Tag::getId).collect(Collectors.toList());
                //将旧的和新的合并在一起
                tagIds.addAll(newTagIds);
            }
            //3.保存新标签和关联关系
            List<ArticleTag> articleTags = tagIds.stream().map(item -> ArticleTag.builder().articleId(id).tagId(item).build()).collect(Collectors.toList());
            //保存新的关联关系
            articleTagMapper.insertBatch(articleTags);
        }

    }

    @Transactional
    public Category saveArticleCategory(ArticleVo vo) {
        String categoryName = vo.getCategoryName();
        LambdaQueryWrapper<Category> categoryLambdaQueryWrapper = Wrappers.lambdaQuery(Category.class).eq(Category::getCategoryName, categoryName);
        Category category = categoryMapper.selectOne(categoryLambdaQueryWrapper);
        if (Objects.isNull(category) && !vo.getStatus().equals(ArticleStatusEnum.DRAFT.getStatus())) {
            category = new Category();
            category.setCategoryName(categoryName);
            categoryMapper.insert(category);
        }
        return category;
    }
}
